﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Windows.Markup;
using AppToolkit.WPF.Controls.Utils;

namespace AppToolkit.WPF.Controls
{
    /// <summary>
    /// Interaction logic for BindableRichTextbox.xaml
    /// </summary>
    public partial class RichTextEditor : UserControl
    {

        /// <summary>
        /// Bindable Text property.
        /// </summary>
        public static readonly DependencyProperty TextProperty =
          DependencyProperty.Register("Text", typeof(string), typeof(RichTextEditor),
          new PropertyMetadata(string.Empty, OnMyPropertyChanged));

        private bool _lockUpdate;

        /// <summary>
        /// CTOR
        /// </summary>
        public RichTextEditor()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Text property
        /// </summary>
        public string Text
        {
            get { return GetValue(TextProperty) as string; }
            set { SetValue(TextProperty, value); }
        }

        /// <summary>
        /// Ends editing the text and updates <see cref="Text"/> property.
        /// </summary>
        public void UpdateFlowDocument()
        {
            _lockUpdate = true;
            try
            {
                this.Text = getChangesFromDocument();
            }
            finally
            {
                _lockUpdate = false;
            }
        }

        /// <summary>
        /// Fired when the Document and Control have been updated based on a change
        /// in the Text property.
        /// </summary>
        public event EventHandler DocumentUpdated;

        #region Converter
        private IValueConverter _converter;

        /// <summary>
        /// Allows overriding the serialization format.
        /// </summary>
        public IValueConverter Converter
        {
            get
            {
                if (_converter == null)
                {
                    _converter = new NullConverter();
                }
                return _converter;
            }
            set
            {
                _converter = value;
            }
        } 
        #endregion

        [System.Diagnostics.DebuggerStepThrough]
        private static void OnMyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var richTextEditor = d as RichTextEditor;
            Contract.Assert(richTextEditor != null);

            if (richTextEditor._lockUpdate)
                return;

            richTextEditor.applyChangesToDocument();
        }

        private string getChangesFromDocument()
        {
            var tr = new TextRange(_RTB.Document.ContentStart,
                               _RTB.Document.ContentEnd);

            using (var ms = new MemoryStream())
            {
                tr.Save(ms, DataFormats.Xaml);
                string xamlText = ASCIIEncoding.Default.GetString(ms.ToArray());
                return (string)this.Converter.ConvertBack(xamlText, null, null, null);
            }
        }

        private void applyChangesToDocument()
        {
            var newXAML = (string)this.Converter.Convert(this.Text, null,null,null);

            using (_RTB.DeclareChangeBlock())
            {
                _RTB.Document.Blocks.Clear();

                if (!string.IsNullOrEmpty(newXAML))
                {
                    using (var xamlMemoryStream = new MemoryStream(Encoding.ASCII.GetBytes(newXAML)))
                    {
                        var parser = new ParserContext();
                        parser.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
                        parser.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
                        var doc = new FlowDocument();
                        var section = XamlReader.Load(xamlMemoryStream, parser) as Section;

                        _RTB.Document.Blocks.Add(section);
                    }
                }
            }

            if (this.DocumentUpdated != null)
                this.DocumentUpdated(this, EventArgs.Empty);
        }

        private void OnRTBLostFocus(object sender, RoutedEventArgs e)
        {
            UpdateFlowDocument();
        }

    }
}
